package com.redis.service.impl;

import com.redis.entity.Concert;
import com.redis.entity.Ticket;
import com.redis.mapper.ConcertMapper;
import com.redis.mapper.TicketMapper;
import com.redis.service.TicketService;
import com.redis.util.RedissonBlommerUtil;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.stream.MapRecord;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeUnit;

/**
 * @Author: Li
 * @Date: 2024/12/18 15:43
 * @Description:
 */
@Service
public class TicketServiceImpl implements TicketService {
    @Autowired
    private RedissonBlommerUtil redissonBlommerUtil;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private ConcertMapper concertMapper;
    
    private final String STREAM_KEY = "updateTickets";
    
    public String purchase(Integer uid, Integer cid) {
        // 防止缓存穿透
        if (!redissonBlommerUtil.checkBloomFilter(cid)) {
            return "演唱会不存在";
        }
        
        // 进行购票操作，剩余票数减一
        Integer tickets = null;
        // 对redis的数据加锁，防止缓存击穿，保证数据一致性
        RLock rLock = redissonClient.getLock("concertLock:" + cid);
        try {
            // 加锁
            rLock.lock();
            
            // 从缓存查询
            tickets = getConcertByRedis(cid);
            
            // 缓存中不存在，到数据库查询
            if (tickets == null) {
                Concert concert = concertMapper.getById(cid);
                // 数据库中查询为空，说明不存在该演唱会记录
                if (concert == null) {
                    return "演唱会不存在";
                }
                // 演唱会存在，保存到redis
                saveToRedis(cid, concert.getTickets());
            }
            
            tickets = getConcertByRedis(cid);
            // 余票大于1
            if (tickets >= 1) {
                tickets -= 1;
                // 更新redis
                redisTemplate.opsForValue().decrement("concert:" + cid);
                // 异步更新mysql，减少加锁的阻塞时间
                // 发送更新消息到消息队列
                Map<String, Object> message = Map.of("cid", cid, "uid", uid, "tickets", tickets);
                // 将消息写入 Stream
                redisTemplate.opsForStream().add(MapRecord.create(STREAM_KEY, message));
            } else {
                return "余票不足";
            }
        } catch (Exception e) {
            return "下单异常，剩余：" + tickets;
        } finally {
            // 释放锁
            rLock.unlock();
        }
        
        return "购票成功, 剩余：" + tickets;
    }
    
    private void saveToRedis(Integer cid, Integer tickets) {
        String key = "concert:" + cid;
        redisTemplate.opsForValue().set(key, tickets);
        
        // 加100秒随机值，防止缓存雪崩
        Integer random = new Random().nextInt(100);
        redisTemplate.expire(key, 360 + random, TimeUnit.SECONDS);
    }
    
    private Integer getConcertByRedis(Integer cid) {
        String key = "concert:" + cid;
        // 延长过期时间，避免数据库未更新完缓存就过期
        Integer random = new Random().nextInt(100);
        redisTemplate.expire(key, 360 + random, TimeUnit.SECONDS);
        return (Integer) redisTemplate.opsForValue().get(key);
    }
    
    public List<Concert> getAllConcert() {
        return concertMapper.getAllConcert();
    }
}
